package com.zrkizzy.module.storage.service;

import cn.hutool.core.collection.CollectionUtil;
import com.zrkizzy.common.core.utils.IdUtil;
import com.zrkizzy.common.core.utils.bean.BeanCopyUtil;
import com.zrkizzy.common.models.domain.system.file.File;
import com.zrkizzy.common.models.dto.system.file.FileDTO;
import com.zrkizzy.common.models.dto.system.file.FileUploadDTO;
import com.zrkizzy.common.models.vo.system.file.FileUploadStrategyVO;
import com.zrkizzy.common.storage.dto.FileUrlDTO;
import com.zrkizzy.common.storage.enums.UploadStrategy;
import com.zrkizzy.common.storage.factory.FileUploadFactory;
import com.zrkizzy.common.storage.strategy.context.FileUploadStrategyContext;
import com.zrkizzy.common.storage.template.AbstractFileUploadStrategy;
import com.zrkizzy.module.security.utils.SecurityUtil;
import com.zrkizzy.module.storage.mapper.FileMapper;
import com.zrkizzy.storage.facade.service.IFileService;
import com.zrkizzy.storage.facade.service.IFileTypeService;
import com.zrkizzy.system.facade.service.config.ISystemConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
 * 文件业务逻辑接口实现类
 *
 * @author zhangrongkang
 * @since 2023/5/12
 */
@Slf4j
@Service
public class FileServiceImpl implements IFileService {

    @Autowired
    private ISystemConfigService systemConfigService;

    @Autowired
    private IFileTypeService fileTypeService;

    @Autowired
    private FileUploadStrategyContext fileUploadStrategyContext;

    @Autowired
    private IdUtil idUtil;

    @Autowired
    private SecurityUtil securityUtil;

    @Autowired
    private FileUploadFactory fileUploadFactory;

    @Autowired
    private FileMapper fileMapper;

    /**
     * 获取文件上传策略集合
     *
     * @return 文件上传策略返回集合
     */
    @Override
    public List<FileUploadStrategyVO> listUploadStrategy() {
        List<FileUploadStrategyVO> result = new ArrayList<>();
        // 遍历文件上传策略枚举
        for (UploadStrategy uploadStrategy : UploadStrategy.values()) {
            // 构建文件上传策略返回对象
            result.add(FileUploadStrategyVO.builder().strategy(uploadStrategy.getStrategy()).mark(uploadStrategy.getMark()).build());
        }
        return result;
    }

    /**
     * 保存文件数据
     *
     * @param fileDTO 文件数据传输对象
     * @return 受影响行数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer save(FileDTO fileDTO) {
        // 复制文件对象
        File file = BeanCopyUtil.copy(fileDTO, File.class);
        file.setId(idUtil.nextId());
        // 根据ID判断更新或新增，执行对应操作后返回受影响的行数
        return fileMapper.insert(file);
    }

    /**
     * 文本编辑器上传图片
     *
     * @param fileUploadDTO 文件数据传输对象
     * @return 图片访问路径
     */
    @Override
    public String addImage(FileUploadDTO fileUploadDTO) {
        String classify = fileTypeService.getPathByFileTypeId(fileUploadDTO.getFileTypeId());
        // 上传策略
        UploadStrategy uploadStrategy = getUploadStrategy();
        AbstractFileUploadStrategy fileUploadStrategy = fileUploadFactory.getInstance(uploadStrategy);
        // 文件路径
        String filePath = fileUploadStrategy.getFilePath(fileUploadDTO.getFile(), classify);
        // 存在当前文件则直接返回当前文件的访问路径
        if (fileUploadStrategy.exist(filePath)) {
            return fileUploadStrategy.getAccessUrl(filePath);
        }
        // 上传图片并返回图片访问路径
        return upload(fileUploadDTO);
    }

    /**
     * 批量删除文件
     *
     * @param fileList 文件集合
     * @return 是否删除成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(List<FileDTO> fileList) {
        // 没有选择文件直接返回TRUE
        if (CollectionUtil.isEmpty(fileList)) {
            return Boolean.TRUE;
        }
        // 上传策略
        UploadStrategy uploadStrategy = getUploadStrategy();
        List<Long> ids = new ArrayList<>();
        List<String> filePaths = new ArrayList<>();
        for (FileDTO fileDTO : fileList) {
            ids.add(fileDTO.getId());
            filePaths.add(fileDTO.getPath());
        }
        return fileUploadStrategyContext.executeDeleteStrategy(filePaths, uploadStrategy) && deleteBatch(ids);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String upload(FileUploadDTO fileUploadDTO) {
        // 获取文件分类路径
        String classify = fileTypeService.getPathByFileTypeId(fileUploadDTO.getFileTypeId());
        // 获取文件上传策略
        UploadStrategy uploadStrategy = getUploadStrategy();
        // 上传文件
        FileUrlDTO fileUrlDTO = fileUploadStrategyContext.executeUploadStrategy(fileUploadDTO.getFile(), classify, uploadStrategy);
        // 保存文件上传数据
        save(setFileDtoAttribute(fileUploadDTO, fileUrlDTO, uploadStrategy.getMark()));
        // 返回文件的访问域名
        return fileUrlDTO.getAccessUrl();
    }

    /**
     * 批量删除文件信息
     *
     * @param ids 文件ID集合
     * @return 是否删除成功
     */
    private Boolean deleteBatch(List<Long> ids) {
        // 返回受影响行数与文件ID集合数量是否相同
        return fileMapper.deleteBatchIds(ids) == ids.size();
    }

    /**
     * 设置文件保存数据对象属性值
     *
     * @param fileUploadDTO 文件上传数据传输对象
     * @param fileUrlDTO 文件路径数据传输对象
     * @param uploadStrategy 文件上传策略
     * @return 文件保存数据对象
     */
    private FileDTO setFileDtoAttribute(FileUploadDTO fileUploadDTO, FileUrlDTO fileUrlDTO, String uploadStrategy) {
        return FileDTO.builder()
                // 文件分类ID
                .fileTypeId(fileUploadDTO.getFileTypeId())
                // 文件名称
                .name(fileUrlDTO.getFileName())
                // 文件存储路径
                .path(fileUrlDTO.getPath())
                // 文件访问路径
                .src(fileUrlDTO.getAccessUrl())
                // 文件MD5哈希值，用于文件唯一标识
                .md5(fileUrlDTO.getMd5())
                // 文件类型
                .type(fileUrlDTO.getType())
                // 上传用户ID
                .userId(securityUtil.getLoginUser().getId())
                // 上传策略
                .strategy(uploadStrategy)
                // 文件大小
                .size(fileUploadDTO.getFile().getSize()).build();
    }

    /**
     * 获取文件上传策略
     *
     * @return 上传策略
     */
    private UploadStrategy getUploadStrategy() {
        String mark = systemConfigService.getSystemConfig().getUploadStrategy();
        return UploadStrategy.getUploadStrategyByMark(mark);
    }

}
